package com.zcoj.service.impl;

import cn.hutool.core.lang.UUID;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zcoj.core.model.ApiResponse;
import com.zcoj.core.model.PageData;
import com.zcoj.core.model.PageWrap;
import com.zcoj.mail.pojo.MailLog;
import com.zcoj.mail.service.EmailService;
import com.zcoj.mapper.JudgeMapper;
import com.zcoj.mapper.UserAcProblemMapper;
import com.zcoj.mapper.UserMapper;
import com.zcoj.mapper.UserRoleMapper;
import com.zcoj.pojo.Dto.*;
import com.zcoj.pojo.Vo.LoginPageVo;
import com.zcoj.pojo.Vo.RegisterPageVo;
import com.zcoj.pojo.Vo.UserInfoVO;
import com.zcoj.security.dto.LoginUser;
import com.zcoj.security.service.LoginServcie;
import com.zcoj.service.RoleService;
import com.zcoj.service.UserService;
import com.zcoj.util.RedisCaches;
import com.zcoj.util.WrapperUtil;
import com.zcoj.wx.pojo.WxQrCode;
import com.zcoj.wx.pojo.WxUser;
import com.zcoj.wx.service.WxQrCodeService;
import com.zcoj.wx.service.WxUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.mail.MessagingException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Service实现
 * @author 剩xia的盛夏
 * @date 2023/02/24 16:16
 */
@Service
@Slf4j
public class UserServiceImpl implements UserService {


    @Autowired
    private RedisCaches redisCaches;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private LoginServcie loginServcie;

    @Autowired
    private EmailService emailService;

    @Autowired
    private JudgeMapper judgeMapper;

    @Autowired
    private UserRoleMapper userRoleMapper;

    @Autowired
    private RoleService roleService;

    @Autowired
    private WxUserService wxUserService;

    @Autowired
    private WxQrCodeService wxQrCodeService;

    @Autowired
    private UserAcProblemMapper userAcProblemMapper;

    @Autowired
    private BCryptPasswordEncoder bcryptPasswordEncoder;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Value("${spring.mail.username}")
    private String fromEmail;

    @Override
    public Integer create(User user) {
        userMapper.insert(user);
        Role role = new Role();
        role.setRole("user");
        Role role1 = roleService.findOne(role);
        if(Objects.nonNull(role1)){
            UserRole userRole = new UserRole();
            userRole.setUid(user.getId());
            userRole.setRid(role1.getId());
            userRoleMapper.insert(userRole);
        }
        return user.getId();
    }

    @Override
    public User findByUserName(String username) {
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("username",username);
        System.out.println(username);
        return userMapper.selectOne(queryWrapper);
    }

    @Override
    public Integer deleteById(Integer id) {
        return userMapper.deleteById(id);
    }

    @Override
    public void deleteByIdInBatch(List<Integer> ids) {
        if (CollectionUtils.isEmpty(ids)) return;
        userMapper.deleteBatchIds(ids);
    }

    @Override
    public Integer updateById(User user) {
        return userMapper.updateById(WrapperUtil.blankToNull(user));
    }

    /**
     * @method: [saveUserInfo]
     * @param: [user]
     * @returns: java.lang.Integer
     * @description:  用户保存信息（去除密码，邮箱，OI分数，状态，避免非法修改）
     * @author: chen.bin
     * @version: 1.0.0
     * @date 2023/5/11 16:28
     **/
    @Override
    public Integer saveUserInfo(User user) {
        user.setPassword(null);
        user.setEmail(null);
        user.setOiScore(null);
        user.setStatus(null);
        if(Objects.nonNull(user.getUsername())){
            QueryWrapper<User> userQueryWrapper=new QueryWrapper<>();
            userQueryWrapper.eq("username",user.getUsername()).ne("id",user.getId());
            Integer selectCount = userMapper.selectCount(userQueryWrapper);
            if(selectCount>0){
                return 0;
            }else {
                return updateById(user);
            }
        }else {
            return 0;
        }
    }


    @Override
    public void updateByIdInBatch(List<User> users) {
        if (CollectionUtils.isEmpty(users)) return;
        for (User user: users) {
            this.updateById(user);
        }
    }

    @Override
    public UserInfoVO getUserInfoByID(Integer uid) {
        User user = findById(uid);
        UserInfoVO userInfoVO = new UserInfoVO();
        if(Objects.nonNull(user)){
            userInfoVO.setUid(user.getId())
                    .setUsername(user.getUsername())
                    .setNickname(user.getNickname())
                    .setRealname(user.getRealname())
                    .setFaceUrl(user.getFaceUrl())
                    .setSignature(user.getSignature())
                    .setOiScore(user.getOiScore());
            //查找角色字符串
            List<String> roleList=new ArrayList<>();
            QueryWrapper<UserRole> userRoleQueryWrapper=new QueryWrapper<>();
            userRoleQueryWrapper.eq("uid",uid);
            List<UserRole> userRoleList = userRoleMapper.selectList(userRoleQueryWrapper);

            for (UserRole userRole : userRoleList) {
                Role role = roleService.findById(userRole.getRid());
                roleList.add(role.getRole());
            }
            userInfoVO.setRoles(roleList);
            QueryWrapper<Judge> judgeQueryWrapper=new QueryWrapper<>();
            judgeQueryWrapper.eq("uid",uid);
            Integer submitCount = judgeMapper.selectCount(judgeQueryWrapper);
            judgeQueryWrapper.eq("status",0);
            Integer accessCount = judgeMapper.selectCount(judgeQueryWrapper);
            userInfoVO.setSubmitCount(submitCount)
                    .setAccessCount(accessCount);
            List<Integer> pids=new ArrayList<>();
            QueryWrapper<UserAcProblem> userAcProblemQueryWrapper=new QueryWrapper<>();
            userAcProblemQueryWrapper.eq("uid",uid);
            List<UserAcProblem> userAcProblems = userAcProblemMapper.selectList(userAcProblemQueryWrapper);

            for (UserAcProblem userAcProblem : userAcProblems) {
                pids.add(userAcProblem.getPid());
            }
            userInfoVO.setAccessProblemID(pids);

            return userInfoVO;
        }else {
            return userInfoVO;
        }
    }

    @Override
    public User findById(Integer id) {
        return userMapper.selectById(id);
    }

    @Override
    public User findOne(User user) {
        Wrapper<User> wrapper = new QueryWrapper<>(user);
        return userMapper.selectOne(wrapper);
    }

    @Override
    public List<User> findList(User user) {
        Wrapper<User> wrapper = new QueryWrapper<>(user);
        return userMapper.selectList(wrapper);
    }
  
    @Override
    public PageData<User> findPage(PageWrap<User> pageWrap) {
        IPage<User> page = new Page<>(pageWrap.getPage(), pageWrap.getCapacity());
        User user = WrapperUtil.blankToNull(pageWrap.getModel());

        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        for(PageWrap.SortData sortData: pageWrap.getSorts()) {
            if (sortData.getDirection().equalsIgnoreCase("DESC")) {
                queryWrapper.orderByDesc(sortData.getProperty());
            } else {
                queryWrapper.orderByAsc(sortData.getProperty());
            }
        }
        if(Objects.nonNull(user)&&Objects.nonNull(user.getUsername())){
            queryWrapper.like("username",user.getUsername()).or().like("nickname",user.getUsername()).or().like("realname",user.getUsername());
        }
        if(Objects.nonNull(user)&&Objects.nonNull(user.getSex())){
            queryWrapper.eq("sex",user.getSex());
        }
        if(Objects.nonNull(user)&&Objects.nonNull(user.getStatus())){
            queryWrapper.eq("status",user.getStatus());
        }
        return PageData.from(userMapper.selectPage(page, queryWrapper));
    }

    @Override
    public long count(User user) {
        Wrapper<User> wrapper = new QueryWrapper<>(user);
        return userMapper.selectCount(wrapper);
    }

    @Override
    public User findByEmail(String email) {
        QueryWrapper<User> wrapper =new QueryWrapper<>();
        wrapper.eq("email",email);
        return userMapper.selectOne(wrapper);
    }

    /**
     * @method: [revisePassword]
     * @param: [map]
     * @returns: java.lang.Integer
     * @description: passwordForm: {
     *         oldPassword: '',
     *         newPassword: '',
     *         tempPassword:''
     *       },
     * @author: chen.bin
     * @version: 1.0.0
     * @date 2023/5/11 16:38
     **/
    @Override
    public ApiResponse revisePassword(Map<String, String> map) {
        String oldPassword = map.get("oldPassword");
        String newPassword = map.get("newPassword");
        String tempPassword = map.get("tempPassword");
        if(Objects.nonNull(tempPassword)&&Objects.nonNull(oldPassword)&&Objects.nonNull(newPassword)){
            if(Objects.equals(tempPassword,newPassword)){
                LoginUser loginUser = redisCaches.getLoginUser();
                log.info("loginUser:{}",loginUser.getPassword());
                log.info("map:{}",bcryptPasswordEncoder.encode(oldPassword));
                log.info("map:{}",oldPassword);
                if(passwordEncoder.matches(oldPassword, loginUser.getPassword())) {
                    User user = new User();
                    user.setPassword(bcryptPasswordEncoder.encode(newPassword));
                    user.setId(loginUser.getId());
                    int integer = userMapper.updateById(user);
                    if(integer>0){
                        return ApiResponse.success("修改成功",null);
                    }else {
                        return ApiResponse.failed("修改失败");
                    }
                }else {
                    return ApiResponse.failed("密码错误");
                }

            }
        }
            return ApiResponse.failed("参数错误");

    }

    /**
     * @method: [reviseEmail]
     * @param: [map]
     * @returns: com.zcoj.core.model.ApiResponse
     * @description:  用户修改邮箱
     *  emailForm: {
     *         newEmail: '',
     *         code: '',
     *       },
     * @author: chen.bin
     * @version: 1.0.0
     * @date 2023/5/11 17:24
     **/
    @Override
    public ApiResponse reviseEmail(Map<String, String> map) {
        String newEmail = map.get("newEmail");
        String code = map.get("code");
        if(Objects.nonNull(newEmail)&&Objects.nonNull(code)){
            String redisEmailKey="emailCode_"+newEmail;
            String cacheObject = redisCaches.getCacheObject(redisEmailKey);
            if(!(Objects.nonNull(cacheObject)&&Objects.equals(cacheObject,code))){
                return ApiResponse.failed("校验失败！！！");
            }
            QueryWrapper<User> userQueryWrapper=new QueryWrapper<>();
            userQueryWrapper.eq("email",newEmail);
            Integer selectCount = userMapper.selectCount(userQueryWrapper);
            if(selectCount==0){
                LoginUser loginUser = redisCaches.getLoginUser();
                User user = new User();
                user.setId(loginUser.getId());
                user.setEmail(newEmail);
                Integer integer = updateById(user);
                if(integer>0){
                    redisCaches.deleteObject(redisEmailKey);
                    return ApiResponse.success("修改邮箱成功",null);
                }else {
                    return ApiResponse.failed("修改失败");
                }
            }else {
                return ApiResponse.failed("邮箱已经被绑定");
            }
        }
        return ApiResponse.failed("参数错误");
    }

    @Override
    public ApiResponse checkUserWxBind(Integer uid) {
        String openId=null;
        WxUser wxUserByUid = wxUserService.findWxUserByUid(uid);
        if (Objects.nonNull(wxUserByUid)){
            openId=wxUserByUid.getOpenId();
        }
        return ApiResponse.success(openId);
    }

    @Override
    public ApiResponse setWxUserInfo(String ticket, Integer uid) {
        Map<String, String> map=new HashMap<>();
        WxQrCode wxQrCode = wxQrCodeService.getWxQrCodeByTicket(ticket);
        if (Objects.isNull(wxQrCode)){
            map.put("status","noPass");
            map.put("msg","ticket参数异常");
            return ApiResponse.success(map);
        }else {
            if (wxQrCode.getIsPass().equals(-1)){
                return ApiResponse.success(null);
            }
            if (wxQrCode.getIsPass().equals(1)){
                WxUser wxUser = new WxUser();
                wxUser.setUid(uid);
                wxUser.setOpenId(wxQrCode.getOpenId());
                wxUserService.insertWxUser(wxUser);
                map.put("status","Pass");
                map.put("msg","微信绑定成功");
                return ApiResponse.success(map);
            }
            if (wxQrCode.getIsExpire().equals(1)){
                map.put("status","noPass");
                map.put("msg","ticket过期,请重新加载二维码");
                return ApiResponse.success(map);
            }
            map.put("status","noPass");
            map.put("msg","未知错误！");
            return ApiResponse.success(map);
        }
    }

    @Override
    public ApiResponse verificationCode(LoginPageVo loginPageVo) {
        User user = userMapper.findByUserName(loginPageVo.getUsername());
        if (Objects.isNull(user)){
            return ApiResponse.failed("用户不存在");
        }
        if(Objects.nonNull(user.getStatus())&&user.getStatus()==0){
            return ApiResponse.failed("用户被封禁");
        }
        if (Objects.isNull(user.getEmail())){
            return ApiResponse.failed("未绑定邮箱");
        }
        String redisEmailKey="emailCode_"+user.getEmail();
        String redisCode = redisCaches.getCacheObject(redisEmailKey);
        log.info(redisCode);
        if (Objects.isNull(redisCode)){
            return ApiResponse.failed("验证码失效");
        }
        if(!redisCode.equals(loginPageVo.getCode())){
            return ApiResponse.failed("验证码错误");
        }else{
            redisCaches.deleteObject(redisEmailKey);
            log.info("{}验证码移除成功{}",redisEmailKey,redisCode);
            user.setPassword(loginPageVo.getPassword());
            return loginServcie.loginUsernamePassword(user);
        }

    }

    @Override
    public ApiResponse loginByWxTicket(String ticket) {
        return loginServcie.loginUserByWxTicket(ticket);
    }

    /**
     * @method: [adminLogin]
     * @param: [map]
     * @returns: com.zcoj.core.model.ApiResponse
     * @description:
     * formLogin: {
     *         username: '',
     *         password: '',
     *       },
     * @author: chen.bin
     * @version: 1.0.0
     * @date 2023/5/12 23:40
     **/
    @Override
    public ApiResponse adminLogin(Map<String, String> map) {
        String username = map.get("username");
        String password = map.get("password");
        User user = findByUserName(username);
        if(Objects.nonNull(user)){
            QueryWrapper<UserRole> queryWrapper=new QueryWrapper<>();
            queryWrapper.eq("uid",user.getId());
            List<UserRole> userRoleList = userRoleMapper.selectList(queryWrapper);
            List<String> stringList=new ArrayList<>();
            for (UserRole userRole : userRoleList) {
                Role role = roleService.findById(userRole.getRid());
                stringList.add(role.getRole());
            }
            if(stringList.contains("super_admin")||stringList.contains("admin")){
                user.setPassword(password);
                return loginServcie.loginUsernamePassword(user);
            }else {
                return ApiResponse.failed("权限不足");
            }
        }
        return ApiResponse.failed("用户不存在");
    }

    @Override
    public ApiResponse retrievePassword(LoginPageVo loginPageVo) {
        User user = userMapper.findByUserName(loginPageVo.getUsername());
        if (Objects.isNull(user)){
            return ApiResponse.failed("用户不存在");
        }
        if (Objects.isNull(user.getEmail())){
            return ApiResponse.failed("未绑定邮箱");
        }
        String redisEmailKey="emailCode_"+user.getEmail();
        String redisCode = redisCaches.getCacheObject(redisEmailKey);
        if (Objects.isNull(redisCode)){
            return ApiResponse.failed("验证码失效");
        }
        if(!redisCode.equals(loginPageVo.getCode())){
            return ApiResponse.failed("验证码错误");
        }else{
            redisCaches.deleteObject(redisEmailKey);
            log.info("{}验证码移除成功{}",redisEmailKey,redisCode);
            UpdateWrapper<User> userUpdateWrapper=new UpdateWrapper<>();
            userUpdateWrapper.set("password",bcryptPasswordEncoder.encode(loginPageVo.getPassword())).eq("id",user.getId());
            int updateStatus = userMapper.update(null,userUpdateWrapper);
            if (updateStatus==1){
                return ApiResponse.success("修改成功",null);
            }else {
                return ApiResponse.failed("修改失败");
            }
        }
    }

    @Override
    public ApiResponse registerUser(RegisterPageVo registerPageVo) {
        QueryWrapper<User> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("username",registerPageVo.getUsername()).or().eq("email",registerPageVo.getEmail());
        Integer selectCount = userMapper.selectCount(queryWrapper);
        if (selectCount!=0){
            return ApiResponse.failed("用户名已存在或邮箱已经绑定");
        }
        String redisEmailKey="emailCode_"+registerPageVo.getEmail();
        String redisCode = redisCaches.getCacheObject(redisEmailKey);
        if (Objects.isNull(redisCode)){
            return ApiResponse.failed("验证码失效");
        }
        if(!redisCode.equals(registerPageVo.getCode())){
            return ApiResponse.failed("验证码错误");
        }else{
            redisCaches.deleteObject(redisEmailKey);
            log.info("{}验证码移除成功{}",redisEmailKey,redisCode);
            User user=new User();
            user.setPassword(bcryptPasswordEncoder.encode(registerPageVo.getPassword()));
            user.setUsername(registerPageVo.getUsername());
            user.setEmail(registerPageVo.getEmail());
            Integer insertStatus = create(user);
            if (Objects.nonNull(insertStatus)){
                return ApiResponse.success("注册成功",null);
            }else {
                return ApiResponse.failed("注册失败");
            }
        }
    }

    @Override
    public ApiResponse sendEmailCodePost(LoginPageVo loginPageVo) {
        User user = userMapper.findByUserName(loginPageVo.getUsername());
        if(Objects.isNull(user)){
            return ApiResponse.failed("用户不存在");
        }else if(Objects.isNull(user.getEmail())){
            return ApiResponse.failed("未绑定邮箱");
        }else {
            String redisEmailKey="emailCode_"+user.getEmail();
            if(Objects.nonNull(redisCaches.getCacheObject(redisEmailKey))){
                return ApiResponse.failed("验证码未失效");
            }else{
                String code= UUID.randomUUID().toString().substring(0,4);
                System.out.println(code);
                //验证码提交到rabbitMq
                MailLog mailLog = new MailLog();
                mailLog.setFromMail(fromEmail);
                mailLog.setCode(code);
                mailLog.setToMail(user.getEmail());
                mailLog.setUsername(user.getUsername());
                try {
                    emailService.sendThymeleafLoginMail(mailLog);
                } catch (MessagingException e) {
                    throw new RuntimeException(e);
                }
                redisCaches.setCacheObject(redisEmailKey,code,10, TimeUnit.MINUTES);
                return ApiResponse.success("发送成功",null);
            }
        }

    }

    @Override
    public Integer sendEmailCode(String email) {
        String redisEmailKey="emailCode_"+email;
        if(Objects.nonNull(redisCaches.getCacheObject(redisEmailKey))){
            return 0;//未失效
        }else{
            String code= UUID.randomUUID().toString().substring(0,4);
            System.out.println(code);
            MailLog mailLog = new MailLog();
            mailLog.setFromMail(fromEmail);
            mailLog.setCode(code);
            mailLog.setToMail(email);
            mailLog.setUsername(email);
            try {
                emailService.sendThymeleafLoginMail(mailLog);
            } catch (MessagingException e) {
                throw new RuntimeException(e);
            }
            redisCaches.setCacheObject(redisEmailKey,code,10, TimeUnit.MINUTES);
            return 1;
        }
    }

    @Override
    public ApiResponse logout() {
        return loginServcie.logout();
    }

}
